#include <QApplication>
#include <QUuid>
#include <QDir>
#include <QFile>
#include <QIODevice>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDateTime>
#include "tlvpkg.h"
#include "tcpclientthread.h"
#include "net_api_main.h"
#include "LocalCmdMap.h"

// 静态成员变量
const QByteArray TLVContent::m_splitFlag = "@#";
QString TLVPkg::m_filePath = "/home/yx/WorkSpace/program/";
const QString TLVPkg::m_databaseName = "QSQLITE";
// TcpClientThread对象指针
void *TLVPkg::m_pClient = nullptr;
void *TLVPkg::m_pNetApi = nullptr;
QString TLVPkg::m_logPath = "/opt/ponted/apps/app1/var/BSCS.log";

// 静态变量
static const char g_xmlDeclaration[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
static const int g_maxFloderCount = 4;
static const auto g_noDotAndLinksfilter = QDir::NoDotAndDotDot | QDir::NoSymLinks;
static QSqlDatabase g_databaseSqlite;
//static const QString g_rootPath = "program";
static const QString g_rootPath = "pro";
static QString g_dbPath = "/home/yx/WorkSpace/NetMiddleWare/BSCS.db3";
static QString g_devID = "0001";
static QMutex g_mutex;


QVector<bool(*)(const TLVContent &content, tinyxml2::XMLDocument &doc,
                tinyxml2::XMLElement *cmd)> TLVPkg::m_handleCmdFuncs = {
        TLVPkg::handlePostUserInfo,             // 用户信息
        TLVPkg::handlePostVirsualInfo,          // 目测结论
        TLVPkg::handleDataResultAck,            // 结果数据
        TLVPkg::handleRealTimeDataAck,          // 实时数据
        TLVPkg::handleStepDataAck,              // 过程数据
        TLVPkg::handleDeviceStatusAck,          // 设备状态
        TLVPkg::handleOpenCloseRecordAck,       // 开关机记录
        nullptr,// 设置信息
        nullptr,// 血袋数据
        TLVPkg::handleReqDirsFiles,             // 查询文件夹及文件
        TLVPkg::handleAddFile,                  // 增加文件
        TLVPkg::handleDelFile,                  // 删除文件
        TLVPkg::handleUseDirFile,               // 应用文件夹和文件
        TLVPkg::handleReqLogFile,               // 日志上传
};

TLVContent::TLVContent(const QString &guid, const QString &msgType,
                       const QString &devId, const QString &pageMsg,
                       const QString &xmlContent) {
    m_guid = guid;
    m_msgType = msgType;
    m_devId = devId;
    m_pageMsg = pageMsg;
    m_xmlContent = xmlContent;
}

TLVContent::TLVContent(const QByteArray &content) {
    if (content.length() < 8 || !content.startsWith(m_splitFlag)) {
        m_parsingResult = false;
        return;
    }
    try {
        int guidEndIndex = content.indexOf(m_splitFlag, 1);
        ushort splitLen = m_splitFlag.length();
        m_guid = content.mid(splitLen, guidEndIndex - splitLen);
        int msgTypeEndIndex = content.indexOf(m_splitFlag, guidEndIndex + splitLen);
        m_msgType = content.mid(guidEndIndex + splitLen, msgTypeEndIndex - guidEndIndex - splitLen);
        int devIdEndIndex = content.indexOf(m_splitFlag, msgTypeEndIndex + splitLen);
        m_devId = content.mid(msgTypeEndIndex + splitLen, devIdEndIndex - msgTypeEndIndex - splitLen);
        int pageMsgEndIndex = content.indexOf(m_splitFlag, devIdEndIndex + splitLen);
        m_pageMsg = content.mid(devIdEndIndex + splitLen, pageMsgEndIndex - devIdEndIndex - splitLen);
        m_xmlContent = QString::fromUtf8(content.mid(pageMsgEndIndex + splitLen));
        qDebug() << " TLVContent: " << content << "\n"
                 << "m_guid: " << m_guid << "\n"
                 << "m_msgType: " << m_msgType << "\n"
                 << "m_devId: " << m_devId << "\n"
                 << "m_pageMsg: " << m_pageMsg << "\n"
                 << "m_xmlContent: " << m_xmlContent << "\n";
    } catch (std::out_of_range error) {
        m_parsingResult = false;
    } catch (std::runtime_error error) {
        m_parsingResult = false;
    } catch (...) {
        m_parsingResult = false;
    }
}

QByteArray TLVContent::toByteArray() {
    QByteArray bytes;
    bytes.append(m_splitFlag);
    bytes.append(m_guid.toUtf8());
    bytes.append(m_splitFlag);
    bytes.append(m_msgType.toUtf8());
    bytes.append(m_splitFlag);
    bytes.append(m_devId.toUtf8());
    bytes.append(m_splitFlag);
    bytes.append(m_pageMsg.toUtf8());
    bytes.append(m_splitFlag);
    bytes.append(m_xmlContent.toUtf8());
    return bytes;
}


DeviceStatueReq::DeviceStatueReq(const QString &ip,
                const QString &devID,
                const int &pos,
                const int &status,
                const QString &msg)
{
    m_pos = pos;
    m_status = status;
    m_ip = ip;
    m_devId = devID;
    m_msg = msg;
    m_getTime = QDateTime::currentDateTime().toString("yyyyMMdd hh:mm:ss");
}

QByteArray DeviceStatueReq::toByteArray()
{
    TLVHead head;
    TLVContent reqContent;
    reqContent.m_guid = QUuid::createUuid().toString();
    reqContent.m_msgType = "1";
    reqContent.m_devId = g_devID;
    reqContent.m_pageMsg = "1/1";

    using namespace tinyxml2;
    XMLDocument docReq;
    docReq.Parse(g_xmlDeclaration);
    XMLElement *root = docReq.NewElement("Req");
    docReq.InsertEndChild(root);

    XMLElement *cmdType = docReq.NewElement("CmdType");
    int cmdTypeValue = DeviceStatueReq::cmdype;
    cmdType->SetText(cmdTypeValue);
    root->InsertEndChild(cmdType);

    XMLElement *ipElement = docReq.NewElement("IP");
    ipElement->SetText(m_ip.toUtf8().data());
    root->InsertEndChild(ipElement);

    XMLElement *devIDElement = docReq.NewElement("DevID");
    devIDElement->SetText(m_devId.toUtf8().data());
    root->InsertEndChild(devIDElement);

    XMLElement *posElement = docReq.NewElement("Pos");
    posElement->SetText(m_pos);
    root->InsertEndChild(posElement);

    XMLElement *statusElement = docReq.NewElement("Status");
    statusElement->SetText(m_status);
    root->InsertEndChild(statusElement);

    XMLElement *msgElement = docReq.NewElement("Msg");
    msgElement->SetText(m_msg.toUtf8().data());
    root->InsertEndChild(msgElement);

    XMLElement *getTimeElement = docReq.NewElement("GetTime");
    getTimeElement->SetText(m_getTime.toUtf8().data());
    root->InsertEndChild(getTimeElement);

    XMLPrinter printer;
    docReq.Print(&printer);
    QString xml(printer.CStr());
    reqContent.m_xmlContent = xml;

    QByteArray contentBytes = reqContent.toByteArray();
    int tlvValeLen = contentBytes.length();
    head.m_length[3] = (tlvValeLen >> 24) & 0xFF;
    head.m_length[2] = (tlvValeLen >> 16) & 0xFF;
    head.m_length[1] = (tlvValeLen >> 8) & 0xFF;
    head.m_length[0] = tlvValeLen & 0xFF;
    QByteArray bytes;
    size_t headLen = sizeof(TLVHead);
    auto pChar = reinterpret_cast<char*>(&head);
    for (size_t i=0; i<headLen; ++i) {
        bytes.append(pChar[i]);
    }
    bytes.append(contentBytes);
    return bytes;
}

QByteArray TLVPkg::mapToByteArray(const QVariantMap &map)
{
    TLVHead head;
    TLVContent reqContent;
    reqContent.m_guid = QUuid::createUuid().toString();
    reqContent.m_msgType = "1";
    reqContent.m_devId = g_devID;
    reqContent.m_pageMsg = "1/1";

    using namespace tinyxml2;
    XMLDocument docReq;
    docReq.Parse(g_xmlDeclaration);
    XMLElement *root = docReq.NewElement("Req");
    docReq.InsertEndChild(root);

    for (const QString &name : map.keys()) {
        XMLElement *element = docReq.NewElement(name.toUtf8().data());
        element->SetText(map[name].toString().toUtf8().data());
        root->InsertEndChild(element);
    }

    XMLPrinter printer;
    docReq.Print(&printer);
    QString xml(printer.CStr());
    reqContent.m_xmlContent = xml;

    QByteArray contentBytes = reqContent.toByteArray();
    int tlvValeLen = contentBytes.length();
    head.m_length[3] = (tlvValeLen >> 24) & 0xFF;
    head.m_length[2] = (tlvValeLen >> 16) & 0xFF;
    head.m_length[1] = (tlvValeLen >> 8) & 0xFF;
    head.m_length[0] = tlvValeLen & 0xFF;
    QByteArray bytes;
    size_t headLen = sizeof(TLVHead);
    auto pChar = reinterpret_cast<char*>(&head);
    for (size_t i=0; i<headLen; ++i) {
        bytes.append(pChar[i]);
    }
    bytes.append(contentBytes);
    return bytes;
}

QByteArray TLVPkg::userInfoReq()
{
    TLVHead head;
    TLVContent reqContent;
    reqContent.m_guid = QUuid::createUuid().toString();
    reqContent.m_msgType = "1";
    reqContent.m_devId = g_devID;
    reqContent.m_pageMsg = "1/1";

    using namespace tinyxml2;
    XMLDocument docReq;
    docReq.Parse(g_xmlDeclaration);
    XMLElement *root = docReq.NewElement("Req");
    docReq.InsertEndChild(root);

    XMLElement *cmdType = docReq.NewElement("CmdType");
    int cmdTypeValue = m_handleCmdFuncs.indexOf(TLVPkg::handlePostUserInfo);
    cmdType->SetText(cmdTypeValue);
    root->InsertEndChild(cmdType);

    XMLPrinter printer;
    docReq.Print(&printer);
    QString xml(printer.CStr());
    reqContent.m_xmlContent = xml;

    QByteArray contentBytes = reqContent.toByteArray();
    int tlvValeLen = contentBytes.length();
    head.m_length[3] = (tlvValeLen >> 24) & 0xFF;
    head.m_length[2] = (tlvValeLen >> 16) & 0xFF;
    head.m_length[1] = (tlvValeLen >> 8) & 0xFF;
    head.m_length[0] = tlvValeLen & 0xFF;
    QByteArray bytes;
    size_t headLen = sizeof(TLVHead);
    auto pChar = reinterpret_cast<char*>(&head);
    for (size_t i=0; i<headLen; ++i) {
        bytes.append(pChar[i]);
    }
    bytes.append(contentBytes);
    return bytes;
}

QByteArray TLVPkg::virsualInfoReq()
{
    TLVHead head;
    TLVContent reqContent;
    reqContent.m_guid = QUuid::createUuid().toString();
    reqContent.m_msgType = "1";
    reqContent.m_devId = g_devID;
    reqContent.m_pageMsg = "1/1";

    using namespace tinyxml2;
    XMLDocument docReq;
    docReq.Parse(g_xmlDeclaration);
    XMLElement *root = docReq.NewElement("Req");
    docReq.InsertEndChild(root);

    XMLElement *cmdType = docReq.NewElement("CmdType");
    int cmdTypeValue = m_handleCmdFuncs.indexOf(TLVPkg::handlePostVirsualInfo);
    cmdType->SetText(cmdTypeValue);
    root->InsertEndChild(cmdType);

    XMLPrinter printer;
    docReq.Print(&printer);
    QString xml(printer.CStr());
    reqContent.m_xmlContent = xml;

    QByteArray contentBytes = reqContent.toByteArray();
    int tlvValeLen = contentBytes.length();
    head.m_length[3] = (tlvValeLen >> 24) & 0xFF;
    head.m_length[2] = (tlvValeLen >> 16) & 0xFF;
    head.m_length[1] = (tlvValeLen >> 8) & 0xFF;
    head.m_length[0] = tlvValeLen & 0xFF;
    QByteArray bytes;
    size_t headLen = sizeof(TLVHead);
    auto pChar = reinterpret_cast<char*>(&head);
    for (size_t i=0; i<headLen; ++i) {
        bytes.append(pChar[i]);
    }
    bytes.append(contentBytes);
    return bytes;
}

TLVPkg::TLVPkg(const QByteArray &data)
{
    Q_UNUSED(data);
}

TLVPkg::~TLVPkg()
{

}

void TLVPkg::setProgramPath(const QString &path)
{
    QMutexLocker locker(&g_mutex);
    m_filePath = path;
    if (!path.endsWith("/")) {
        m_filePath += "/";
    }
}
void TLVPkg::setDBFilePath(const QString &path)
{
    QMutexLocker locker(&g_mutex);
    g_dbPath = path;
}

void TLVPkg::setDevId(const QString &devID)
{
    QMutexLocker locker(&g_mutex);
    g_devID = devID;
}

void TLVPkg::setLogPath(const QString &path)
{
    TLVPkg::m_logPath = path;
}

bool TLVPkg::settingInfo(QString &ip, int &port, QString &devID,
                         const QString &dbPath, QString &msg)
{
    QMutexLocker locker(&g_mutex);
    g_dbPath = dbPath;
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        QSqlError error = g_databaseSqlite.lastError();
        msg = error.text();
        qDebug() << msg;
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    query.exec("SELECT Value AS ip FROM SetInfo si WHERE \"Key\"= 'ServIP';");
    QString pcIp;
    while (query.next()) {
        pcIp = query.value("ip").toString();
    }

    query.exec("SELECT  Value  AS port FROM  SetInfo si WHERE  \"Key\"='ServPort';");
    int pcPort;
    while (query.next()) {
        pcPort = query.value("port").toInt();
    }

    query.exec("SELECT  Value  AS devID FROM  SetInfo si WHERE  \"Key\"='DeviceID';");
    QString _devID;
    while (query.next()) {
        _devID = query.value("devID").toString();
    }

    g_databaseSqlite.commit();
    QSqlError error = g_databaseSqlite.lastError();
    bool isValid = g_databaseSqlite.isValid();
    g_databaseSqlite.close();
    ip = pcIp;
    port = pcPort;
    devID = _devID;
    msg = error.text();
    return isValid;
}

void TLVPkg::setNetAPI(void *pApi)
{
    QMutexLocker locker(&g_mutex);
    m_pNetApi = pApi;
}


MsgType TLVPkg::isReq(const TLVContent &content) {
    auto type = content.m_msgType.toUInt();
    if (type == 0) {
        return MsgType::REQ;
    } else if (type == 1) {
        return MsgType::ACK;
    } else {
        return MsgType::ERROR;
    }
}

bool TLVPkg::verifyPkg(const QByteArray &tlvHead)
{
    TLVHead *pTLVHead = reinterpret_cast<TLVHead*>(const_cast<char*>(tlvHead.data()));
    if (pTLVHead->m_tag[0] == 0xA9 && pTLVHead->m_tag[1] == 0x00
            && pTLVHead->m_type[0] == 0x0 && pTLVHead->m_type[1] ==0) {
        int sum = 0;
        int len = sizeof(pTLVHead->m_length)/sizeof(uchar);
        for (int i=0; i< len; ++i) {
            sum += pTLVHead->m_length[i];
        }
        if (sum != 0) {
            return true;
        }
    }
    return false;
}

long TLVPkg::tlvValueLen(const QByteArray &tlvHead)
{
    TLVHead *pTLVHead = reinterpret_cast<TLVHead*>(const_cast<char*>(tlvHead.data()));
    long bite0, bite1, bite2, bite3;
    bite0 = pTLVHead->m_length[0];
    bite1 = pTLVHead->m_length[1];
    bite2 = pTLVHead->m_length[2];
    bite3 = pTLVHead->m_length[3];
    long len = bite3;
    len = len << 8;
    len |= bite2;
    len = len << 8;
    len |= bite1;
    len = len << 8;
    len |= bite0;
    qDebug() << __FUNCTION__ << ": " << len;
    return len;
}

TLVContent TLVPkg::parsingTlvValue(const QByteArray &tlvValue)
{
    return TLVContent(tlvValue);
}

bool TLVPkg::handleTask(const TLVContent &content,
                        void *pClient)
{
    QMutexLocker locker(&g_mutex);
    m_pClient = pClient;
    // 解析XML然后得到CmdType,然后分发任务
    tinyxml2::XMLElement *cmdElement = nullptr;
    tinyxml2::XMLDocument doc;
    int cmd = cmdTypeFromXml(content, doc, &cmdElement);
    if (cmd < 0 || cmd > m_handleCmdFuncs.length()) {
        qDebug() << __FUNCTION__ << ": Error xml: "  << content.m_xmlContent;
        return false;
    }
    qDebug() << __FUNCTION__ << " xml: " << content.m_xmlContent;
    auto handleFunc = m_handleCmdFuncs[cmd];
    if (handleFunc != nullptr) {
        handleFunc(content, doc, cmdElement);
    }
    doc.Clear();
    return false;
}

bool TLVPkg::handleReqDirsFiles(const TLVContent &xmlcontent,
                                tinyxml2::XMLDocument &doc,
                                tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(cmd);
    // 没有需要解析的，所以提前释放doc
    doc.Clear();
    // 查询数据库内文件夹及文件
    QList<QStringList> filesPath = selectFilesFromDb();

    // 发送第一包, 所有的文件夹和文件信息
    using namespace tinyxml2;
    XMLDocument docAck;
    docAck.Parse(g_xmlDeclaration);
    XMLElement *root = docAck.NewElement("Ack");
    docAck.InsertEndChild(root);

    XMLElement *cmdType = docAck.NewElement("CmdType");
    int cmdTypeValue = m_handleCmdFuncs.indexOf(TLVPkg::handleReqDirsFiles);
    cmdType->SetText(cmdTypeValue);
    root->InsertEndChild(cmdType);

    XMLElement *arrayOfFilePath = docAck.NewElement("ArrayOfFilePath");
    for (QStringList &record: filesPath) {
        XMLElement *fileElement = docAck.NewElement("FilePath");
        QString fileName = record.at(0);
        QString folder = record.at(1);
        QString path = folder.mid(folder.lastIndexOf('/') + 1);
        path = (path == g_rootPath) ? "" : path + "/";
        fileElement->SetText(QString(path + fileName).toUtf8().data());
        arrayOfFilePath->InsertEndChild(fileElement);
    }
    root->InsertEndChild(arrayOfFilePath);

    XMLPrinter printer;
    docAck.Print(&printer);
    QString xml(printer.CStr());
    // 将封装好的XML发送出去
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    int fileCount = filesPath.length() + 1;
    QByteArray pkgTlv = pkgFromTLV(xmlcontent, xml,
                                   QString("1/%1").arg(fileCount));
    pClient->sendData(pkgTlv);

    // 循环发送文件
    docAck.Clear();
    for (QStringList &record : filesPath) {
        QApplication::processEvents();
        docAck.Parse(g_xmlDeclaration);
        XMLElement *root = docAck.NewElement("Ack");
        docAck.InsertEndChild(root);

        XMLElement *cmdType = docAck.NewElement("CmdType");
        int cmdTypeValue = m_handleCmdFuncs.indexOf(TLVPkg::handleReqDirsFiles);
        cmdType->SetText(cmdTypeValue);
        root->InsertEndChild(cmdType);

        XMLElement *fileElement = docAck.NewElement("FilePath");
        QString fileName = record.at(0);
        QString folder = record.at(1);
        QString path = folder.mid(folder.lastIndexOf('/') + 1);
        path = (path == g_rootPath) ? "" : path + "/";
        fileElement->SetText(QString(path +  fileName).toUtf8().data());
        root->InsertEndChild(fileElement);

        XMLElement *fileContentElement = docAck.NewElement("FileContent");
        // 填充文件内容
        QString content = readFile(path + fileName);
        fileContentElement->SetText(content.toUtf8().data());
        root->InsertEndChild(fileContentElement);

        XMLPrinter printer;
        docAck.Print(&printer);
        QString xml(printer.CStr());
        QString pageMsg = QString("%1/%2").arg(filesPath.indexOf(record)+2).arg(fileCount);
        QByteArray pkgTlv = pkgFromTLV(xmlcontent, xml, pageMsg);
        pClient->sendData(pkgTlv);
    }

    return true;
}

bool TLVPkg::handleAddFile(const TLVContent &xmlcontent,
                           tinyxml2::XMLDocument &doc,
                           tinyxml2::XMLElement *cmd)
{
    // 解析需要增加的文件名称和文件内容
    using namespace tinyxml2;

    // 文件路径
    XMLElement *filePathElement = cmd->NextSiblingElement("FilePath");
    QString filePathValue = QString::fromUtf8(filePathElement->GetText());

    // 文件内容
    XMLElement *fileContentElement = cmd->NextSiblingElement("FileContent");
    QString fileContentValue = QString::fromUtf8(fileContentElement->GetText());
    doc.Clear();

    // 执行写入文件及数据库, 判断执行结果
    QString error, dbError;
    bool re = writeFile(filePathValue, fileContentValue, error);
    if (re) {
        re &= addFileToDB(filePathValue, dbError);
    }

    // 封装执行结果的应答xml
    int cmdType = cmdTypeFromXml(xmlcontent, doc, &cmd);
    QString xml = ackCommonXml(cmdType, re ? 1 : 0, error + dbError);
    // 将封装好的XML发送出去
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    QByteArray pkgTlv = pkgFromTLV(xmlcontent, xml, xmlcontent.m_pageMsg);
    pClient->sendData(pkgTlv);
    QStringList pageMsgList = xmlcontent.m_pageMsg.trimmed().split("/");
    if (pageMsgList.at(0) == pageMsgList.at(1)){
        // 发送更新制备程序的信号
        pClient->recvSyncData(g_cmdMap[PROGRAM_FILE_UPDATE]);
    }
    return re;
}

bool TLVPkg::handleDelFile(const TLVContent &xmlcontent,
                           tinyxml2::XMLDocument &doc,
                           tinyxml2::XMLElement *cmd)
{
    QVector<QString> filePaths;
    using namespace tinyxml2;
    XMLElement *arrayOfDir = cmd->NextSiblingElement("ArrayOfFilePath");
    if (arrayOfDir == nullptr) {
        return false;
    }
    XMLElement *directorElement = arrayOfDir->FirstChildElement();
    while (directorElement != nullptr) {
        filePaths.append(QString::fromUtf8(directorElement->GetText()));
        directorElement = directorElement->NextSiblingElement();
    }
    doc.Clear();

    // 删除文件任务, 判断执行结果
    QVector<QString> deletedFiles;
    QString error;
    bool re = rmFiles(filePaths, deletedFiles, error);
    if (re) {
        // 更新数据库
        for (QString &filePath : deletedFiles) {
            re &= delFileFromDB(filePath, error);
        }
    }

    // 封装执行结果的应答xml
    QString xml = ackCommonXml(m_handleCmdFuncs.indexOf(handleDelFile),
                               re ? 1: 0, error);
    // 将封装好的XML放入发送队列中
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    QByteArray pkgTlv = pkgFromTLV(xmlcontent, xml);
    pClient->sendData(pkgTlv);
    // 发送更新制备程序的信号
    pClient->recvSyncData(g_cmdMap[PROGRAM_FILE_UPDATE]);
    return false;
}

bool TLVPkg::handleUseDirFile(const TLVContent &xmlcontent,
                              tinyxml2::XMLDocument &doc,
                              tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(cmd);
    using namespace tinyxml2;
    QString error;
    bool re = true;
    QStringList pageMsgList = xmlcontent.m_pageMsg.trimmed().split("/");
    // 根据应答的数据来判断是否要删除所有制备程序, m_pageMsg == "1/n"
    if (pageMsgList.at(0) == "1") {
        re = clearFileAndDB(error);
    }

    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    if (re) {
        // 处理添加文件, handleAddFile里面处理了回应, 里面处理了发送更新分离机制备程序信号
        handleAddFile(xmlcontent, doc, cmd);
    } else {
        // 封装执行结果的应答xml
        QString xml = ackCommonXml(m_handleCmdFuncs.indexOf(handleUseDirFile), 0, error);
        // 将封装好的XML放入发送队列中
        QByteArray pkgTlv = pkgFromTLV(xmlcontent, xml, xmlcontent.m_pageMsg);
        pClient->sendData(pkgTlv);
    }
    return true;
}

bool TLVPkg::handleDeviceStatusAck(const TLVContent &content,
                           tinyxml2::XMLDocument &doc,
                           tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(content);
    Q_UNUSED(doc);
    Q_UNUSED(cmd);

    return true;
}

int TLVPkg::cmdTypeFromXml(const TLVContent &content,
                           tinyxml2::XMLDocument &doc,
                           tinyxml2::XMLElement **cmd)
{
    // 去解析CmdType的值, 然后分发处理任务
    MsgType msgType = static_cast<MsgType>(content.m_msgType.toInt());
    QString root = msgType == MsgType::ACK ? "Ack" : "Req";
    using namespace tinyxml2;
    XMLError xmlError = doc.Parse(content.m_xmlContent.toUtf8());
    if (xmlError == XMLError::XML_SUCCESS) {
        XMLElement *rootElement = doc.RootElement();
        if (rootElement == nullptr) {
            return -1;
        }
        *cmd = rootElement->FirstChildElement("CmdType");
        if (*cmd == nullptr) {
            return -1;
        }
        bool ok = false;
        int cmdValue = QString((*cmd)->GetText()).toInt(&ok);
        return ok ? cmdValue : -1;
    }
    return -1;
}

QString TLVPkg::ackCommonXml(const int &cmd, const int &result,
                             const QString &msg)
{
    // 封装执行结果的应答xml
    using namespace tinyxml2;
    XMLDocument docAck;
    docAck.Parse(g_xmlDeclaration);
    XMLElement *root = docAck.NewElement("Ack");
    docAck.InsertEndChild(root);

    XMLElement *cmdType = docAck.NewElement("CmdType");
    cmdType->SetText(cmd);
    root->InsertEndChild(cmdType);
    XMLElement *resultElement = docAck.NewElement("Result");
    resultElement->SetText(result);
    root->InsertEndChild(resultElement);
    XMLElement *errorElement = docAck.NewElement("ErrorMsg");
    errorElement->SetText(msg.toUtf8().data());
    root->InsertEndChild(errorElement);

    XMLPrinter printer;
    docAck.Print(&printer);
    QString xml(printer.CStr());
    return xml;
}

bool TLVPkg::rmFiles(QVector<QString> &filesPath,
                     QVector<QString> &deletedFile,
                     QString &error)
{
    for (const QString &path : filesPath) {
        QFile file(QString(m_filePath + path).toUtf8());
        if (file.exists()) {
            if (!file.remove()) {
                error += QString(u8"删除%1文件失败!").arg(path);
                continue;
            }
        }
        deletedFile.append(path);
    }
    // 清空没有文件的文件夹
    QDir dir(m_filePath);
    auto dirsPath = dir.entryList(QDir::AllDirs | g_noDotAndLinksfilter);
    for (QString &path : dirsPath) {
        QDir subDir(m_filePath + path);
        uint fileCount = subDir.entryList(QDir::Files | g_noDotAndLinksfilter).length();
        if (fileCount <= 0) {
            qDebug() << m_filePath + path;
            if (!subDir.rmdir(QString(m_filePath + path).toUtf8())) {
                error += QString(u8"删除%1文件夹失败!").arg(path);
                return false;
            }
        }
    }
    return true;
}

bool TLVPkg::addDirs(QVector<QString> &dirs)
{
    for (const QString &dir : dirs) {
        QDir dirPath(m_filePath + dir);
        if (dirPath.exists()) {
            return false;
        }
        if (!dirPath.mkdir(dirPath.absolutePath().toUtf8())) {
            return false;
        }
    }
    return true;
}

bool TLVPkg::writeFile(const QString &filePath,
                       const QString &content,
                       QString &error)
{
    QFile file(QString(m_filePath + filePath).toUtf8());
    if (filePath.contains("/")) {
        // 1. 如果要添加的文件是在Program的子目录下, 则要检查目录存在性及是否超过4个目录
        // 文件目录不存在则创建目录，如果目录超过4个则失败
        if (!isAllDirOrFile(m_filePath, true)) {
            error = u8"分离机Program直接根目录下存在文件，不能创建包含子文件夹路径的文件!";
            return false;
        }
        QString dirPath = m_filePath + filePath.left(filePath.indexOf('/'));
        QDir dir(dirPath.toUtf8());
        if (!dir.exists()) {
            QDir rootDir(m_filePath);
            // 存在.和..目录
            auto len = rootDir.entryList(QDir::Dirs | g_noDotAndLinksfilter).length();
            if (len >= g_maxFloderCount) {
                error = u8"分离机已经存在四个目录，无法创建新目录!";
                return false;
            }
            if (!dir.mkdir(dirPath.toUtf8())) {
                error = u8"分离机上新目录创建失败!";
                return false;
            }
        }
    } else {
        // 2. 判断要添加的文件是否是Program直接路径下?
        // 如果添加文件是在Program目录下则要检查Program下是否全是文件不存在目录，有文件夹则添加失败
        // 如果Program下全是文件，则直接添加
        if (!isAllDirOrFile(m_filePath, false)) {
            error = u8"分离机Program目录下存在文件夹，不能直接在根目录增加制备文件!";
            return false;
        }
    }
    if (file.open(QIODevice::WriteOnly)) {
        file.write(content.toUtf8());
        file.close();
        return true;
    } else{
        error = QString(u8"指定的文件:%1无法创建!").arg(filePath);
        return false;
    }
}

QString TLVPkg::readFile(const QString &filePath, bool isAbsolutePath)
{
    QString _path = isAbsolutePath ? filePath : m_filePath + filePath;
    QFile file(_path);
    if (!file.exists()) {
        return "";
    }
    if (file.open(QIODevice::ReadOnly)) {
        QTextStream in(&file);
        in.setCodec("utf-8");
        QString tmp = in.readAll();
//        QString tmp = file.readAll();
        file.close();
        return tmp;
    }
    return "";
}

bool TLVPkg::addFileToDB(const QString &filePath, QString &error)
{
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        error = u8"分离机本地数据库无法打开!";
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    // 查询最大OrderID
    int maxID = 0;
    query.exec(u8"SELECT MAX(OrderID) AS max_id FROM PrgFileInfo pfi;");
    while (query.next()) {
        maxID = query.value("max_id").toInt();
    }
    // 插入文件记录
    QString fileName = fileNameFromFilePath(filePath);
    QString folderName = folderNameFromFilePath(filePath);
    QString sql = QString(u8"INSERT INTO PrgFileInfo (OrderID, FileName, Folder) \
                          VALUES (%1, \'%2\', \'%3\');").arg(maxID + 1).arg(fileName).arg(folderName);
    qDebug() << sql;
    query.exec(sql.toUtf8());
    // 提交
    g_databaseSqlite.commit();
    bool isValid = g_databaseSqlite.isValid();
    if (isValid) {
        error = "";
    } else {
        QSqlError sqlError = g_databaseSqlite.lastError();
        error = sqlError.text();
    }
    g_databaseSqlite.close();
    return isValid;
}

bool TLVPkg::delFileFromDB(const QString &filePath, QString &error)
{
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();

    // 查询文件夹和文件是否存在
    bool isExist = false;
    QString fileName = fileNameFromFilePath(filePath);
    QString folderName = folderNameFromFilePath(filePath);
    QString sql = QString(u8"SELECT COUNT(*) AS file_count FROM PrgFileInfo pfi \
                          WHERE FileName=\'%1\' AND Folder=\'%2\';").arg(fileName).arg(folderName);
    qDebug() << sql;
    query.exec(sql);
    while (query.next()) {
        isExist = query.value("file_count").toInt() > 0;
    }
    if (!isExist) {
        return true;
    }
    // 执行删除文件
    QString delSql = QString("DELETE FROM PrgFileInfo \
                             WHERE FileName=\'%1\' AND Folder=\'%2\';").arg(fileName).arg(folderName);
    query.exec(delSql);
    // 提交
    g_databaseSqlite.commit();
    bool isValid = g_databaseSqlite.isValid();
    if (isValid) {
        error = "";
    } else {
        QSqlError sqlError = g_databaseSqlite.lastError();
        error = sqlError.text();
    }
    g_databaseSqlite.close();
    return isValid;
}

QList<QStringList> TLVPkg::selectFilesFromDb()
{
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        QSqlError error = g_databaseSqlite.lastError();
        qDebug() << error.text();
        return QList<QStringList>();
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    query.exec("SELECT FileName, Folder FROM PrgFileInfo pfi;");
    QList<QStringList> filesPath;
    while (query.next()) {
        QString fileName = query.value("FileName").toString();
        QString folder = query.value("Folder").toString();
        filesPath.append({fileName.toUtf8(), folder.toUtf8()});
    }
    g_databaseSqlite.commit();
    QSqlError error = g_databaseSqlite.lastError();
    bool isValid = g_databaseSqlite.isValid();
    g_databaseSqlite.close();
    return isValid ? filesPath : QList<QStringList>();
}

QByteArray TLVPkg::pkgFromTLV(const TLVContent &content,
                              const QString &xml,
                              const QString pageMsg,
                              const bool &isAck)
{
    TLVHead head;
    TLVContent ackContent;
    ackContent.m_guid = content.m_guid;
    ackContent.m_msgType = isAck ? "1" : "0";
    ackContent.m_devId = g_devID;
    ackContent.m_pageMsg = pageMsg;
    ackContent.m_xmlContent = xml;
    QByteArray contentBytes = ackContent.toByteArray();
    int tlvValeLen = contentBytes.length();
    head.m_length[3] = (tlvValeLen >> 24) & 0xFF;
    head.m_length[2] = (tlvValeLen >> 16) & 0xFF;
    head.m_length[1] = (tlvValeLen >> 8) & 0xFF;
    head.m_length[0] = tlvValeLen & 0xFF;
    QByteArray bytes;
    size_t headLen = sizeof(TLVHead);
    auto pChar = reinterpret_cast<char*>(&head);
    for (size_t i=0; i<headLen; ++i) {
        bytes.append(pChar[i]);
    }
    bytes.append(contentBytes);
    return bytes;
}

bool TLVPkg::isAllDirOrFile(const QString &path, const bool &isAllDir)
{
    QDir dir(path);
    // program目录为空，则可以随意添加文件夹或文件，因此返回空
    auto filter = QDir::AllDirs | QDir::Files | g_noDotAndLinksfilter;
    if (dir.entryList(filter).length() <= 0) {
        return true;
    }
    if (isAllDir) {
        // 全文件夹
        if (dir.entryList(g_noDotAndLinksfilter | QDir::Files).length() > 0) {
            return false;
        }
        return true;
    } else {
        // 全文件
        if (dir.entryList(g_noDotAndLinksfilter | QDir::AllDirs).length() > 0) {
            return false;
        }
        return true;
    }
}

QString TLVPkg::fileNameFromFilePath(const QString &filePath)
{
    if (!filePath.contains("/")) {
        return filePath;
    } else {
        return filePath.mid(filePath.lastIndexOf('/') + 1);
    }
}

QString TLVPkg::folderNameFromFilePath(const QString &filePath)
{
    if (!filePath.contains("/")) {
        return m_filePath;
    } else {
        return m_filePath + filePath.mid(0, filePath.lastIndexOf('/'));
    }
}

bool TLVPkg::clearFileAndDB(QString &error)
{
    // 清空Program下所以文件夹及文件
    QDir dir(m_filePath);
    if (!dir.exists()) {
        // 异常情况下，program文件夹被删除，无法重建，在重试过程检查是否要重建
        if (!dir.mkdir(m_filePath)) {
            error += u8"创建program文件夹失败, 请重试!";
            return false;
        }
    }
    bool re = dir.removeRecursively();
    if (!re) {
        // 清空制备程序失败
        error += u8"清空制备程序失败, 请重试，或者重启分离机重试!";
        return false;
    }
    if (!dir.mkdir(m_filePath)) {
        error += u8"创建program文件夹失败, 请重试!";
        return false;
    }
    // 清空数据库
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        error += u8"分离机数据库打开失败，请重试!";
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    query.exec("DELETE FROM PrgFileInfo;");
    g_databaseSqlite.commit();
    QSqlError sqlError = g_databaseSqlite.lastError();
    error += sqlError.text();
    g_databaseSqlite.close();
    return true;
}

bool TLVPkg::replaceUserInfoToDB(const QList<QPair<QString, QString> > &userInfos,
                                 QString &error)
{
    // 清空数据库
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        error += u8"分离机数据库打开失败，请重试!";
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    query.exec("DELETE FROM UserInfo;");

    for (auto info : userInfos) {
        QString userID = info.first;
        QString userName = info.second;
        QString sql = QString(u8"INSERT INTO UserInfo (UserID, UserName) \
                              VALUES ('%1', '%2');").arg(userID).arg(userName);
        qDebug() << sql;
        query.exec(sql.toUtf8());
    }

    g_databaseSqlite.commit();
    QSqlError sqlError = g_databaseSqlite.lastError();
    error += sqlError.text();
    g_databaseSqlite.close();
    return true;
}

bool TLVPkg::replaceVirsualInfoToDB(const QList<VisualInfo > &virsualInfos, QString &error)
{
    // 清空数据库
    if (QSqlDatabase::contains(m_databaseName)) {
        g_databaseSqlite = QSqlDatabase::database(m_databaseName);
    } else {
        g_databaseSqlite = QSqlDatabase::addDatabase(m_databaseName);
        g_databaseSqlite.setDatabaseName(g_dbPath);
    }
    if (!g_databaseSqlite.open()) {
        error += u8"分离机数据库打开失败，请重试!";
        return false;
    }
    QSqlQuery query(g_databaseSqlite);

    // 开启事务
    g_databaseSqlite.transaction();
    query.exec("DELETE FROM VisualInfo;");

    for (auto info : virsualInfos) {
        QString sql = QString(u8"INSERT INTO VisualInfo (OrderID, VisualID, VisualName) \
                              VALUES (%1, '%2', '%3');"\
                ).arg(info.OrderID).arg(info.VirsualID).arg(info.VirsualName);
        qDebug() << sql;
        query.exec(sql.toUtf8());
    }

    g_databaseSqlite.commit();
    QSqlError sqlError = g_databaseSqlite.lastError();
    error += sqlError.text();
    g_databaseSqlite.close();
    return true;
}

bool TLVPkg::handleOpenCloseRecordAck(const TLVContent &content,
                                     tinyxml2::XMLDocument &doc,
                                     tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(content);
    Q_UNUSED(doc);
    Q_UNUSED(cmd);
    return true;
}

bool TLVPkg::handleStepDataAck(const TLVContent &content,
                              tinyxml2::XMLDocument &doc,
                              tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(content);
    Q_UNUSED(doc);
    Q_UNUSED(cmd);
    return true;
}

bool TLVPkg::handleRealTimeDataAck(const TLVContent &content,
                                  tinyxml2::XMLDocument &doc,
                                  tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(content);
    Q_UNUSED(doc);
    Q_UNUSED(cmd);
    return true;
}

bool TLVPkg::handleDataResultAck(const TLVContent &content,
                                tinyxml2::XMLDocument &doc,
                                tinyxml2::XMLElement *cmd)
{
    Q_UNUSED(content);
    Q_UNUSED(doc);
    Q_UNUSED(cmd);
    return true;
}


bool TLVPkg::handleReqLogFile(const TLVContent &content,
                              tinyxml2::XMLDocument &doc,
                              tinyxml2::XMLElement *cmd)
{
    // TODO: 先不去处理滚动日志文件，直接读取单个日志文件
    QString logContent = TLVPkg::readFile(m_logPath, true);
    using namespace tinyxml2;
    XMLDocument docAck;
    docAck.Parse(g_xmlDeclaration);
    XMLElement *root = docAck.NewElement("Ack");
    docAck.InsertEndChild(root);

    XMLElement *cmdType = docAck.NewElement("CmdType");
    int cmdTypeValue = m_handleCmdFuncs.indexOf(TLVPkg::handleReqLogFile);
    cmdType->SetText(cmdTypeValue);
    root->InsertEndChild(cmdType);

    XMLElement *fileElement = docAck.NewElement("FilePath");
    QString path = TLVPkg::m_logPath.mid(TLVPkg::m_logPath.lastIndexOf('/') + 1);
    path = (path == g_rootPath) ? "" : path + "/";
    fileElement->SetText(QString(path).toUtf8().data());
    root->InsertEndChild(fileElement);

    XMLElement *fileContentElement = docAck.NewElement("FileContent");
    // 填充文件内容
    fileContentElement->SetText(logContent.toUtf8().data());
    root->InsertEndChild(fileContentElement);

    XMLPrinter printer;
    docAck.Print(&printer);
    QString xml(printer.CStr());
    QByteArray pkgTlv = pkgFromTLV(content, xml);
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    pClient->sendData(pkgTlv);
    return true;
}

bool TLVPkg::handlePostUserInfo(const TLVContent &content,
                                tinyxml2::XMLDocument &doc,
                                tinyxml2::XMLElement *cmd)
{
//    qDebug() << content.m_xmlContent;
    QList<QPair<QString, QString> > userInfos;
    using namespace tinyxml2;
    XMLElement *arrayOfUserInfo = cmd->NextSiblingElement("ArrayOfUserInfo");
    XMLElement *userInfoElemnt = arrayOfUserInfo->FirstChildElement("UserInfo");
    while (userInfoElemnt != nullptr) {
        // UserID
        XMLElement *userIDElemnt = userInfoElemnt->FirstChildElement("UserID");
        // UserName
        XMLElement *userNameElemnt = userInfoElemnt->FirstChildElement("UserName");
        QString userID = userIDElemnt->GetText();
        QString userName = userNameElemnt->GetText();
        qDebug() << "UserID: " << userID << " UserName: " << userName;
        userInfos.append(qMakePair(QString::fromUtf8(userIDElemnt->GetText()),
                                   QString::fromUtf8(userNameElemnt->GetText())));
        userInfoElemnt = arrayOfUserInfo->NextSiblingElement("UserInfo");
    }
    // 发送userInfos
    QString error;
    bool re = replaceUserInfoToDB(userInfos, error);

    // 回应
    // 封装执行结果的应答xml
    int cmdType = cmdTypeFromXml(content, doc, &cmd);
    QString xml = ackCommonXml(cmdType, re ? 1 : 0, error);
    // 将封装好的XML发送出去
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    QByteArray pkgTlv = pkgFromTLV(content, xml, content.m_pageMsg);
    pClient->sendData(pkgTlv);
    pClient->recvSyncData(g_cmdMap[LocalCmd::USER_INFO_UPDATE]);

    return true;
}

bool TLVPkg::handlePostVirsualInfo(const TLVContent &content,
                                   tinyxml2::XMLDocument &doc,
                                   tinyxml2::XMLElement *cmd)
{
    QList<VisualInfo > visualInfos;
    using namespace tinyxml2;
    XMLElement *arrayOfUserInfo = cmd->NextSiblingElement("ArrayOfVisualInfo");
    XMLElement *visualInfoElemnt = arrayOfUserInfo->FirstChildElement("VisualInfo");
    while (visualInfoElemnt != nullptr) {
        // OrderID
        XMLElement *orderIDElemnt = visualInfoElemnt->FirstChildElement("OrderID");
        // VisualID
        XMLElement *visualIDElemnt = visualInfoElemnt->FirstChildElement("VisualID");
        // VisualName
        XMLElement *visualNameElemnt = visualInfoElemnt->FirstChildElement("VisualName");
        visualInfos.append(VisualInfo(QString(orderIDElemnt->GetText()).toInt(),
                                    QString::fromUtf8(visualIDElemnt->GetText()),
                                    QString::fromUtf8(visualNameElemnt->GetText())));
        visualInfoElemnt = arrayOfUserInfo->NextSiblingElement("VisualInfo");
    }
    // 发送visualInfo
    QString error;
    bool re = replaceVirsualInfoToDB(visualInfos, error);

    // 回应
    // 封装执行结果的应答xml
    int cmdType = cmdTypeFromXml(content, doc, &cmd);
    QString xml = ackCommonXml(cmdType, re ? 1 : 0, error);
    // 将封装好的XML发送出去
    TcpClientThread *pClient = static_cast<TcpClientThread*>(m_pClient);
    QByteArray pkgTlv = pkgFromTLV(content, xml, content.m_pageMsg);
    pClient->sendData(pkgTlv);
    pClient->recvSyncData(g_cmdMap[LocalCmd::VIRSUAL_INFO_UPDATE]);

    return true;
}


